#pragma once

#include <string.h>

#include "luster_alloc.hh"
#include "luster_int.hh"
#include "luster_ownership.hh"

namespace luster {

// A contiguous growable array type with heap-allocated contents.
template <typename T> class Vec {
public:
	T const &operator[](UInt i) const {
		return data[i];
	}

	T &operator[](UInt i) {
		return data[i];
	}

	T *data{};
	UInt len{};
	UInt cap{};
};

template <typename T> void vec_clear(Vec<T> *vec) {
	if (vec->cap == 0)
		return;

	drop_range(vec->data, vec->len);

	deallocate(vec->data);

	vec->data = nullptr;
	vec->len = 0;
	vec->cap = 0;
}

luster_DROP_FOR(T, Vec<T>, vec) {
	vec_clear(vec);
};

template <typename T> void vec_reserve(Vec<T> *vec, UInt newcap) {
	if (vec->cap >= newcap)
		return;
	vec->data = (T *)reallocate(vec->data, newcap * sizeof(T));
	vec->cap = newcap;
}

template <typename T> void vec_enlarge(Vec<T> *vec) {
	vec_reserve(vec, vec->cap == 0 ? 4 : vec->cap * 3 / 2);
}

template <typename T> void vec_resize(Vec<T> *vec, UInt newlen) {
	if (newlen == vec->len)
		return;

	if (newlen < vec->len) {
		drop_range(vec->data + newlen, vec->len - newlen);
	} else {
		vec_reserve(vec, newlen);
		memset(vec->data + vec->len, 0,
			sizeof(T) * (newlen - vec->len));
	}
	vec->len = newlen;
}

template <typename T> T *vec_append_uninit(Vec<T> *vec) {
	if (vec->len >= vec->cap) {
		vec_enlarge(vec);
	}

	T *x = vec->data + vec->len;
	vec->len += 1;

	return x;
}

template <typename T> T *vec_append(Vec<T> *vec, T const &data) {
	T *x = vec_append_uninit(vec);
	*x = data;
	return x;
}

template <typename T> T *vec_append_zero(Vec<T> *vec) {
	T *x = vec_append_uninit(vec);
	memset(x, 0, sizeof(T));
	return x;
}

// 弹出 vec 的最后一个元素。当 vec 空时，vec->len 会因为溢出变成 UInt 的
// 最大值。
//
// 因为暂时没有保证 reallocate 之后，[len, cap) 范围内的对象被零初始化，从而不
// 能在它们上面调用 drop。
//
// 所以目前的 vec_clear 只释放了 [0, len) 范围的对象。因此需要在 vec_pop 时 drop
// 被弹出的对象，否则会导致这个对象所有权逃逸。
//
// 如果用户不想要这样，可以额外新增一个 stackend 标记来模拟栈。
template <typename T> void vec_pop(Vec<T> *vec) {
	vec->len -= 1;
	drop(vec->data + vec->len);
}

} // namespace luster
